package com.klondike.common.io;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.concurrent.atomic.AtomicBoolean;

import com.klondike.annotation.Author;
import com.klondike.common.Properties;

/**
 * The class <code>FileProperties</code> is an extension to the standard Java
 * Properties class. It accepts a <code>java.io.File</code> object from which it
 * will read the properties during construction.
 * 
 * Before reading any property subsequently, it will check if the source file
 * has been modified (or not). If file is found to be modified since last
 * loading, it will re-read the file to update itself.
 * 
 */
@Author("Sandeep Kadyan")
public class FileProperties extends Properties {
    private static final long serialVersionUID = 1L;

    private File file;

    private long lastModificationTime;

    private AtomicBoolean reloading = new AtomicBoolean(false);

    private boolean modifiable;

    public FileProperties(Properties defaults, File file, boolean modifiable) throws IOException {
        super(defaults);
        this.file = file;
        this.modifiable = modifiable;
        reload();
    }

    public FileProperties(File file) throws IOException {
        this(null, file, false);
    }

    public void reload() throws IOException {
        long fileTouchTime = file.lastModified();
        if (fileTouchTime > lastModificationTime) {
            try {
                reloading.set(true);
                super.load(new FileReader(file));
                lastModificationTime = fileTouchTime;
            } finally {
                reloading.set(false);
            }
        }
    }

    private void doReloadIfRequired() {
        try {
            reload();
        } catch (IOException e) {
            throw new IllegalStateException("Properties could not be reloaded from " + file, e);
        }
    }

    @Override
    public Object get(Object key) {
        doReloadIfRequired();
        return super.get(key);
    }

    @Override
    public synchronized Object put(Object key, Object value) {
        checkModifiable();

        return super.put(key, value);
    }

    @Override
    public synchronized Object remove(Object key) {
        checkModifiable();
        return super.remove(key);
    }

    private void checkModifiable() {
        if (!modifiable && !reloading.get())
            throw new IllegalStateException(this + " is readonly");
    }

    @Override
    public String toString() {
        return "FileProperties [" + file + "]";
    }

    @Override
    public synchronized void load(Reader reader) throws IOException {
        reload();
    }

    public File getFile() {
        return file;
    }
}
